﻿using System;
using System.Collections.ObjectModel;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using Sautom.CompositionRoot;

namespace Sautom.WcfServer
{
    internal class InjectedServiceHost : ServiceHost
    {
        private readonly IDependencyResolver _resolver;

        protected InjectedServiceHost()
        {
        }

        public InjectedServiceHost(IDependencyResolver resolver, Type serviceType, params Uri[] baseAddresses)
            : base(serviceType, baseAddresses)
        {
            _resolver = resolver;
        }

        protected override void OnOpening()
        {
            Description.Behaviors.Add(new InjectedBehavior(_resolver));
            base.OnOpening();
        }
    }

    internal class InjectedBehavior : IServiceBehavior
    {
        private readonly IDependencyResolver _resolver;
        public InjectedBehavior(IDependencyResolver resolver)
        {
            this._resolver = resolver;
        }

        public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {

        }

        public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
        {

        }

        public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
            foreach (ChannelDispatcher channelDispatcher in serviceHostBase.ChannelDispatchers)
            {
                foreach (var endpoint in channelDispatcher.Endpoints)
                {
                    if (endpoint.ContractName != "IMetadataExchange")
                    {
                        endpoint.DispatchRuntime.InstanceProvider =
                            new InjectedInstanceProvider(_resolver, serviceDescription.ServiceType);
                    }
                }
            }
        }
    }

    internal class InjectedInstanceProvider : IInstanceProvider
    {
        private readonly IDependencyResolver _resolver;
        private readonly Type _contractType;

        public InjectedInstanceProvider(IDependencyResolver resolver, Type contractType)
        {
            this._resolver = resolver;
            this._contractType = contractType;
        }

        public object GetInstance(InstanceContext instanceContext)
        {
            return GetInstance(instanceContext, null);
        }

        public object GetInstance(InstanceContext instanceContext, Message message)
        {
            return _resolver.Resolve(_contractType);
        }

        public void ReleaseInstance(InstanceContext instanceContext, object instance)
        {
            _resolver.Teardown(instance);
        }
    }
}